package org.ccay.ccay_maven_plugin;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzer;
import org.codehaus.plexus.component.annotations.Requirement;

/**
 * initalize Database data;
 */
@Mojo(name = "exportsql", defaultPhase = LifecyclePhase.PROCESS_SOURCES,requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME )
public class Exportsql
    extends AbstractMojo
{
	private static final String SCRIPT_BASE_DIR ="config/dbscript";
	
	private static final String PROPERTIES_PATH = "src/main/resources/app_dev.properties";
	
	private static final Properties Properties = new Properties();
			
	@Parameter( defaultValue = "${project.build.outputDirectory}")
	private String outputdir;
	
    @Parameter(defaultValue = "${project}", readonly = true, required = true)
    private MavenProject project;
    
    
    
    /**
     * 脚本类型-清理
     */
    private static final String SCRIPTTYPE_CLEAN = "clean";
    /**
     *  脚本类型-创建
     */
    private static final String SCRIPTTYPE_INIT = "init";
    
    /**
     * 用于执行数据库清理的SQL文件名
     */
    private static final Set<String> cleanFileNames = new HashSet<String>(); 
    
    /**
     * 用于执行数据库初始化的SQL文件名
     */
    private static final Set<String> initFileNames = new HashSet<String>(); 
    
    StringBuffer cleanSqlBuffer = new StringBuffer();
    
    static{
    	cleanFileNames.add("00.clear.sql");
    	
    	initFileNames.add("01.create_table.sql");
    	initFileNames.add("02.create_sequence.sql");
    	initFileNames.add("03.create_constraint.sql");
    	initFileNames.add("04.create_index.sql");
    	initFileNames.add("05.create_view.sql");
    	initFileNames.add("06.updateddl.sql");
    	initFileNames.add("07.grant.sql");
    	initFileNames.add("08.init_data.sql");
    }
    
    private Map<String,Writer> writeMapper = new HashMap<String,Writer>();
    
    

	/**
     * DependencyAnalyzer
     */
    @Requirement
    private ProjectDependencyAnalyzer dependencyAnalyzer;
	
	public void execute() throws MojoExecutionException {
		getLog().info("paramsfile:"+project.getBasedir().getPath()+"/"+PROPERTIES_PATH);
		getLog().info("outputdir:"+outputdir+"/dbscript");
		try {
			Properties.load(new FileInputStream(project.getBasedir().getPath()+"/"+PROPERTIES_PATH));
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		
		File mysqlDir = new File(outputdir+"/dbscript/mysql");
		if(!mysqlDir.exists()){
			mysqlDir.mkdirs();
		}
		File oracelDire = new File(outputdir+"/dbscript/oracel");
		if(!oracelDire.exists()){
			oracelDire.mkdirs();
		}
		try {
			writeMapper.put("mysql_clean",new OutputStreamWriter(new FileOutputStream(outputdir+"/dbscript/mysql/clean.sql"),"UTF-8"));
			writeMapper.put("mysql_init",new OutputStreamWriter(new FileOutputStream(outputdir+"/dbscript/mysql/init.sql"),"UTF-8"));
			writeMapper.put("oracel_clean",new OutputStreamWriter(new FileOutputStream(outputdir+"/dbscript/oracel/clean.sql"),"UTF-8"));
			writeMapper.put("oracel_init",new OutputStreamWriter(new FileOutputStream(outputdir+"/dbscript/oracel/init.sql"),"UTF-8"));
			scanAndMargeArtifacts(SCRIPTTYPE_CLEAN);
			scanAndMargeArtifacts(SCRIPTTYPE_INIT);
			
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			for(Entry<String, Writer> entry:writeMapper.entrySet()){
				if(entry.getValue()!=null){
					try {
						entry.getValue().close();
					} catch (IOException e) {
						//ignore;
					}
				}
			}
		}
		 
	}
	
	public void scanAndMargeArtifacts(String scriptType){
		Set<Artifact> artifacts = project.getArtifacts();
		if(scriptType.equals(SCRIPTTYPE_INIT)){//顺序
			for(Artifact artifact : artifacts){
				margeFile(artifact,scriptType);
			}
		}else{//倒序
			List<Artifact> temp = new ArrayList<Artifact>();
			for(Artifact artifact : artifacts){
				temp.add(0,artifact);
			}
			for(Artifact artifact : temp){
				margeFile(artifact,scriptType);
			}
		}
	}
	
	public void margeFile(Artifact artifact,String scriptType){
		if(!groupIdMatch(artifact.getGroupId())){
			return ;
		}
		ZipFile zip = null;
		try {
			zip = new ZipFile(artifact.getFile());
		} catch (ZipException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} 
		if(zip == null){
			return ;
		}
		ZipEntry ze;
		Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries();
		while (entries.hasMoreElements()) {  
	        ze = entries.nextElement();  
	        Set<String> fileNames = scriptType.equals(SCRIPTTYPE_CLEAN) ? cleanFileNames : initFileNames;
	        String fileName = ze.getName().substring(ze.getName().lastIndexOf("/")+1);
	        // 读取目标对象  
	        if (ze.getName().startsWith(SCRIPT_BASE_DIR) && fileNames.contains(fileName)) {
	        	String fileDirPath = ze.getName().substring(0,ze.getName().length()-(fileName.length()+1));
	        	String parentDir = fileDirPath.substring(fileDirPath.lastIndexOf("/")+1);
	        	BufferedReader fileReader = null; 
				try {
					fileReader = new BufferedReader(new InputStreamReader(zip.getInputStream(ze),"UTF-8"));
					String line = null;
					boolean addedPath = false;
					while((line = fileReader.readLine()) != null){
						Writer writer = writeMapper.get(parentDir+"_"+scriptType);
						if(line.trim().length()>0 && !addedPath){
							writer.write("/**********"+artifact+ze.getName()+"***********/\n");
							addedPath = true;
						}
						writer.write(replaceParams(line)+"\n");
					}
					
				} catch (IOException e) {
					e.printStackTrace();
				}finally{
					if(fileReader != null){
						try {
							fileReader.close();
						} catch (IOException e) {
							//ignore;
						}
					}
				}
	        }  
	    } 
		try {
			zip.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
    public String replaceParams(String line){
    	String sb = new String(line);
    	Enumeration<String> e = (Enumeration<String>) Properties.propertyNames();
    	while(e.hasMoreElements()){
    		String key = e.nextElement();
    		sb = sb.replace("${"+key+"}", Properties.getProperty(key));
    	}
    	return sb;
    }
	public boolean groupIdMatch(String groupId){
		return groupId.equals("org.ccay") || project.getGroupId().equals(groupId);
	}
	
}
